package com.my.chapter17;

import net.mindview.util.Countries;

import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class practice25 {
    public static void main(String[] args) {
        SimpleHashMap6<String,String> m =
                new SimpleHashMap6<String,String>(),
                m2 = new SimpleHashMap6<String,String>();
        m.putAll(Countries.capitals(10));
        m2.putAll(Countries.capitals(10));
        System.out.println("m.size() = " + m.size());
        System.out.println("m.isEmpty() = " + m.isEmpty());
        System.out.println("m.equals(m2) = " + m.equals(m2));
        System.out.println("m.containsKey(\"BENIN\") = " +
                m.containsKey("BENIN"));
        System.out.println("m.containsKey(\"MARS\") = " +
                m.containsKey("MARS"));
        System.out.println("m.keySet() = " + m.keySet());
        System.out.println("m.values() = " + m.values());
        System.out.println("m = " + m);
        m.remove("CAPE VERDE");
        System.out.println("m = " + m);
    }
}

class SimpleHashMap6<K,V> extends AbstractMap<K,V> {
    static class Entry<K,V> implements Map.Entry<K,V> {
        private K key;
        private V value;
        Entry<K,V> next;

        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
            this.next = null;
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            V result = this.value;
            this.value = value;
            return result;
        }

        public int hashCode() {
            return key.hashCode() ^
                    (value == null ? 0 : value.hashCode());
        }

        public boolean equals(Object o) {
            if(o instanceof Entry) {
                @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>)o;
                Object key1 = getKey();
                Object key2 = e.getKey();
                if(key1.equals(key2)) {
                    Object val1 = getValue();
                    Object val2 = e.getValue();
                    return val1 == null ? val2 == null : val1.equals(val2);
                }
            }
            return false;
        }

        @Override
        public String toString() {
            return key + "=" + value;
        }
    }

    static final int SIZE = 997;
    @SuppressWarnings("unchecked")
    Entry<K,V>[] buckets = new Entry[SIZE];
    public V put(K key, V value) {
        V oldValue = null;
        int index = Math.abs(key.hashCode()) % SIZE;
        Entry<K,V> newPair = new Entry<K,V>(key, value);
        if(buckets[index] == null){
            buckets[index] = newPair;
            return oldValue;
        }
        Entry<K,V> prevPair = null;
        boolean found = false;
        for(Entry<K,V> pair = buckets[index] ; pair != null;
            pair = pair.next) {
            if(pair.getKey().equals(key)) {
                oldValue = pair.getValue();
                if(prevPair != null)
                    prevPair.next = newPair;
                else
                    buckets[index] = newPair;
                newPair.next = pair.next;
                found = true;
                break;
            }
            prevPair = pair;
        }
        if(!found)
            prevPair.next = newPair;
        return oldValue;
    }

    public V get(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        for(Entry<K,V> pair = buckets[index]; pair != null;
            pair = pair.next)
            if(pair.getKey().equals(key))
                return pair.getValue();
        return null;
    }

    @Override
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> set = new HashSet<Map.Entry<K,V>>();
        for(Entry<K,V> bucket : buckets) {
            for(Entry<K,V> pair = bucket; pair != null;
                pair = pair.next)
                set.add(pair);
        }
        return set;
    }

    @SuppressWarnings("unchecked")
    public void clear() {
        buckets = new Entry[SIZE];
    }

    public V remove(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        Entry<K,V> prevPair = null; // Previous element
        for(Entry<K,V> pair = buckets[index] ; pair != null;
            pair = pair.next) {
            if(pair.getKey().equals(key)) {
                V value = pair.getValue();
                if(prevPair != null)
                    prevPair.next = pair.next;
                else
                    buckets[index] = pair.next;
                return value;
            }
            prevPair = pair;
        }
        return null;
    }

    public int size() {
        int sz = 0;
        for(Entry<K,V> bucket : buckets)
            for(Entry<K,V> pair = bucket; pair != null;
                pair = pair.next)
                sz++;
        return sz;
    }

    public boolean isEmpty() {
        for(Entry<K,V> bucket : buckets)
            if(bucket != null)
                return false;
        return true;
    }

    public boolean containsKey(Object key) {
        int index = Math.abs(key.hashCode()) % SIZE;
        if(buckets[index] == null){
            return false;
        }
        for(Entry<K,V> pair = buckets[index] ; pair != null;
            pair = pair.next) {
            if(pair.getKey().equals(key))
                return true;
        }
        return false;
    }
}
